package icraf.carbon.gui;

import icraf.carbon.core.Details;
import icraf.carbon.core.LandCover;
import icraf.carbon.core.LandCoverList;
import icraf.carbon.core.Project;
import icraf.carbon.core.Zone.DataType;
import icraf.carbon.core.ZoneList;

import java.text.DecimalFormat;
import java.text.NumberFormat;

import javax.swing.ListModel;

public class LCTableModel extends MatrixTableModel {
	public enum ValueType {
		TOTAL, TOTAL_NEGATIVE, TOTAL_POSITIVE, ZONE;
	}

	public enum DisplayType {
		MATRIX_VIEW, FORM_VIEW;
	}

	public enum FormType {
		FROM_LC_TO_LIST, TO_LC_FROM_LIST;
	}

	private static final long serialVersionUID = 8968868974903218229L;

	private Object attributes;
	private DataType dataType;
	private boolean editable = true;
	private DecimalFormat formatter = (DecimalFormat) NumberFormat
			.getNumberInstance();
	private boolean isAreaScale = false;
	private boolean isNegativeAllowed = true;
	private boolean isTotalFields = false;
	private LCRowHeaderModel lcRowHeaderModel = new LCRowHeaderModel(this);
	private double multiplier = 1;
	private Project project;
	private ValueType valueType = ValueType.ZONE;

	private int zoneIndex = 0;

	private boolean isZoneArea;
	private DisplayType displayType = DisplayType.MATRIX_VIEW;
	private FormType formType = FormType.FROM_LC_TO_LIST;
	private int landcoverBaseIndex = 0;

	public LCTableModel() {
		super();
	}

	public LCTableModel(boolean isTotalFields) {
		super();
		this.isTotalFields = isTotalFields;
	}

	@Override
	public Class<?> getColumnClass(int columnIndex) {
		return Double.class;
	}

	@Override
	public int getColumnCount() {
		if (project != null) {
			if (displayType == DisplayType.MATRIX_VIEW) {
				if (isTotalFields)
					return project.getLandCoverList().size() + 1;
				return project.getLandCoverList().size();
			} else if (displayType == DisplayType.FORM_VIEW) {
				return 1;
			}
		}
		return 0;
	}

	@Override
	public String getColumnName(int col) {
		if (displayType == DisplayType.MATRIX_VIEW) {
			if (project == null)
				return "";
			if (isTotalFields && col == getColumnCount() - 1) {
				return "TOTAL";
			}
			return project.getLandCoverList().get(col).getLabel();
		} else {
			return "Value";
		}
	}

	public DataType getDataType() {
		return dataType;
	}

	public Details getDetail(int row, int column) {
		LandCoverList l = project.getLandCoverList();
		// TODO: info buat total!
		if (row >= l.size() || column >= l.size())
			return null;
		LandCover lc1;
		LandCover lc2;
		
		if (displayType == DisplayType.MATRIX_VIEW) {
			lc1 = l.get(row);
			lc2 = l.get(column);
		} else {
			LandCover landCoverBase = l.get(landcoverBaseIndex);
			if (formType == FormType.FROM_LC_TO_LIST) {
				lc1 = landCoverBase;
				lc2 = l.get(row);
			} else {
				lc1 = l.get(row);
				lc2 = landCoverBase;
			}
		}
		return project.getZoneList().get(zoneIndex)
				.getDetail(lc1, lc2, dataType, attributes);
	}

	public Project getProject() {
		return project;
	}

	@Override
	public int getRowCount() {
		if(project == null)
			return 0;
		if ((displayType == DisplayType.MATRIX_VIEW)&& isTotalFields)
				return project.getLandCoverList().size() + 1;
		return project.getLandCoverList().size();
	}

	@Override
	public ListModel<Object> getRowHeaderModel() {
		return lcRowHeaderModel;
	}

	@Override
	public Object getValueAt(int row, int column) {
		if (project == null || dataType == null)
			return null;

		if ((displayType == DisplayType.MATRIX_VIEW)&& isTotalFields) {
			if (column == getColumnCount() - 1) {
				double n = 0;
				for (int i = 0; i < getColumnCount() - 1; i++) {
					Double v = (Double) getValueAt(row, i);
					if (v != null)
						n += v;
				}
				return Double.isNaN(n) ? null : n;
			} else if (row == getRowCount() - 1) {
				double n = 0;
				for (int i = 0; i < getRowCount() - 1; i++) {
					Double v = (Double) getValueAt(i, column);
					if (v != null)
						n += v;
				}
				return Double.isNaN(n) ? null : n;
			}
		}

		LandCoverList l = project.getLandCoverList();
		LandCover lcRow;
		LandCover lcCol;
		
		if (displayType == DisplayType.MATRIX_VIEW) {
			lcRow = l.get(row);
			lcCol = l.get(column);
		} else {
			LandCover landCoverBase = l.get(landcoverBaseIndex);
			if (formType == FormType.FROM_LC_TO_LIST) {
				lcRow = landCoverBase;
				lcCol = l.get(row);
			} else {
				lcRow = l.get(row);
				lcCol = landCoverBase;
			}
		}
		
		double val = 0;
		ZoneList zs = project.getZoneList();
//		if (valueType == ValueType.TOTAL) {
//			for (int i = 0; i < zs.getSize(); i++) {
//				double d = 1;
//				if (isAreaScale && isZoneArea ) {
//					d = zs.get(i).getAreaFraction()* project.getTotalArea();
//				}
//				val += zs.get(i).getValue(lcRow, lcCol, dataType, attributes)*d;
//			}
//		} else if (valueType == ValueType.TOTAL_POSITIVE) {
//			for (int i = 0; i < zs.getSize(); i++) {
//				val += Math.max(0,
//						zs.get(i).getValue(lcRow, lcCol, dataType, attributes));
//			}
//		} else if (valueType == ValueType.TOTAL_NEGATIVE) {
//			for (int i = 0; i < zs.getSize(); i++) {
//				val += Math.min(0,
//						zs.get(i).getValue(lcRow, lcCol, dataType, attributes));
//			}
		
		if (valueType == ValueType.TOTAL || valueType == ValueType.TOTAL_POSITIVE || valueType == ValueType.TOTAL_NEGATIVE) {
			for (int i = 0; i < zs.getSize(); i++) {
				double d = 1;
				if (isAreaScale) {
					if(isZoneArea ) {
						d = zs.get(i).getAreaFraction()* project.getTotalArea();
					} else {
						d = project.getTotalArea();
					}
				}
				double valZone = 1;
				if(valueType == ValueType.TOTAL) {
					valZone = zs.get(i).getValue(lcRow, lcCol, dataType, attributes);
				} else if(valueType == ValueType.TOTAL_POSITIVE) {
					valZone = Math.max(0,
							zs.get(i).getValue(lcRow, lcCol, dataType, attributes));
				} else if (valueType == ValueType.TOTAL_NEGATIVE) {
					valZone = Math.min(0,
							zs.get(i).getValue(lcRow, lcCol, dataType, attributes));
				}
				
				val += valZone*d;
			}
			return val;
		} else {
			val = zs.get(zoneIndex)
					.getValue(lcRow, lcCol, dataType, attributes);
		}
		if (Double.isNaN(val))
			return null;
		return val * multiplier;
	}

	public double getValueTotal() {
		double total = 0;
		double x = 0;
		if ((displayType == DisplayType.MATRIX_VIEW)&& isTotalFields) {
			x = 1;
		}
		for (int r = 0; r < getRowCount() - x; r++) {
			for (int c = 0; c < getColumnCount() - x; c++) {
				Object v = getValueAt(r, c);
				if (v != null)
					total += (Double) v;
			}
		}
		return total;
	}

	public ValueType getValueType() {
		return valueType;
	}

	public int getZoneIndex() {
		return zoneIndex;
	}

	public boolean isAreaScale() {
		return isAreaScale;
	}

	@Override
	public boolean isCellEditable(int row, int col) {
		int n = getColumnCount() - 1;
		if ((displayType == DisplayType.MATRIX_VIEW)&& isTotalFields && (col == n || row == n)) {
			return false;
		}
		return editable;
	}

	public boolean isNegativeAllowed() {
		return isNegativeAllowed;
	}

	public boolean isTotalFields() {
		return isTotalFields;
	}

	public void setAreaScale(boolean isAreaScale, boolean isZoneArea) {
		this.isAreaScale = isAreaScale;
		this.isZoneArea = isZoneArea;
		if (isAreaScale && project != null) {
			double d = 1;
			if (isZoneArea ) {
				d = project.getZoneList().get(zoneIndex).getAreaFraction()
						* project.getTotalArea();
			} else {
				d = project.getTotalArea();
			}
			multiplier = d;
		} else {
			multiplier = 1;
		}
		fireTableDataChanged();
	}

	public void setDataType(DataType dataType, Object attributes) {
		this.dataType = dataType;
		this.attributes = attributes;
		fireTableDataChanged();
	}

	public void setEditable(boolean editable) {
		this.editable = editable;
	}

	public void setNegativeAllowed(boolean isNegativeAllowed) {
		this.isNegativeAllowed = isNegativeAllowed;
	}

	public void setProject(Project z) {
		this.project = z;
		lcRowHeaderModel.setProject(z);
		fireTableStructureChanged();
	}

	public void setProject(Project z, DataType t, Object attributes) {
		setDataType(t, attributes);
		setProject(z);
	}

	public void setTotalFields(boolean isTotalFields) {
		this.isTotalFields = isTotalFields;
		fireTableStructureChanged();
	}

	@Override
	public void setValueAt(Object value, int row, int col) {
		if (valueType != ValueType.ZONE)
			return;
		if (value == null)
			return;
		if ((displayType == DisplayType.MATRIX_VIEW)&& isTotalFields
				&& (row == getRowCount() - 1 || col == getColumnCount() - 1))
			return;
		if (multiplier == 0)
			return;
		double d = 0;
		if (value instanceof String) {
			String strVal = (String) value;
			strVal = strVal.replaceAll( "[^\\d.,-Ee]", "" );
			try {
				d = formatter.parse(strVal.trim()).doubleValue();
			} catch (Exception e) {
				return;
			}
		} else {
			d = ((Double) value).doubleValue();
		}
		if (!isNegativeAllowed) {
			d = Math.abs(d);
		}
		LandCoverList l = project.getLandCoverList();
		LandCover lcRow;
		LandCover lcCol;
		
		if (displayType == DisplayType.MATRIX_VIEW) {
			lcRow = l.get(row);
			lcCol = l.get(col);
		} else {
			LandCover landCoverBase = l.get(landcoverBaseIndex);
			if (formType == FormType.FROM_LC_TO_LIST) {
				lcRow = landCoverBase;
				lcCol = l.get(row);
			} else {
				lcRow = l.get(row);
				lcCol = landCoverBase;
			}
		}
		project.getZoneList().get(zoneIndex)
				.setValue(d / multiplier, lcRow, lcCol, dataType, attributes);
		fireTableCellUpdated(row, col);
	}

	public void setValueType(ValueType valueType) {
		this.valueType = valueType;
		fireTableDataChanged();
	}

	public void setZoneIndex(int zoneIndex) {
		this.zoneIndex = zoneIndex;
		fireTableDataChanged();
	}

	public void setProject(Project project, DataType dataType,
			ValueType valueType, Object attributes) {
		setProject(project, dataType, attributes);
		setValueType(valueType);
	}

	public Object getAttributes() {
		return attributes;
	}

	public boolean isEditable() {
		return editable;
	}

	public boolean isZoneArea() {
		return isZoneArea;
	}

	public DisplayType getDisplayType() {
		return displayType;
	}

	public void setDisplayType(DisplayType displayType) {
		this.displayType = displayType;
		fireTableStructureChanged();
	}

	public FormType getFormType() {
		return formType;
	}

	public void setFormType(FormType formType) {
		this.formType = formType;
		fireTableDataChanged();
	}

	public int getLandcoverBaseIndex() {
		return landcoverBaseIndex;
	}

	public void setLandcoverBaseIndex(int landcoverBaseIndex) {
		this.landcoverBaseIndex = landcoverBaseIndex;
		fireTableDataChanged();
	}

	public double getValueScale() {
		return multiplier;
	}
	
	public void resetValue() {
		for (int r = 0; r < getRowCount(); r++) {
			for (int c = 0; c < getColumnCount(); c++) {
				setValueAt(0d, r, c);
			}
		}
	}
}
